﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace server.app
{
    public sealed class NValueManager
    {
        Dictionary<string, comm.objst.NValue> _nvalueDic = new Dictionary<string, comm.objst.NValue>();
        Dictionary<string, comm.objst.NValue> _currNValueDic = new Dictionary<string, comm.objst.NValue>();
        Dictionary<string, List<comm.objst.NValueComputing>> _computingDic = new Dictionary<string, List<comm.objst.NValueComputing>>();

        comm.objst.NValueGroup _group;
        World _world = null;

        public Action<Dictionary<string, long>> OnNvalueChangeCallback = null;

        public NValueManager(World world, comm.objst.NValueGroup group)
        {
            _world = world;
            _group = group;
            InitNValueDic();
        }

        void InitNValueDic()
        {
            lock (this)
            {
                var raw = _world.DefineFile.GetAllNValue(_group);
                foreach (var key in raw.Keys)
                {
                    var item = raw[key];
                    _nvalueDic.Add(key, new comm.objst.NValue
                    {
                        Group = item.Group,
                        Value = item.Value,
                        NType = item.Type
                    });
                }
                InitCurrNValue();
            }
        }
        void InitCurrNValue()
        {
            lock (this)
            {
                _currNValueDic.Clear();
                foreach (var key in _nvalueDic.Keys)
                {
                    var item = _nvalueDic[key];
                    _currNValueDic.Add(key, new comm.objst.NValue
                    {
                        Group = item.Group,
                        Value = item.Value,
                        NType = item.NType
                    });
                }
            }
        }
        void CalcNValue()
        {
            lock (this)
            {
                InitCurrNValue();
                var list = new List<comm.objst.NValueComputing>();
                foreach (var cs in _computingDic.Values)
                {
                    foreach (var c in cs)
                    {
                        list.Add(c);
                    }
                }
                #region sort
                list.Sort(new Comparison<comm.objst.NValueComputing>((n1, n2) =>
                {
                    var v1 = (int)n1.Symbol;
                    var v2 = (int)n2.Symbol;
                    if (v1 > v2)
                    {
                        return 1;
                    }
                    else if (v1 < v2)
                    {
                        return -1;
                    }
                    return 0;
                }));
                #endregion
                foreach (var computing in list)
                {
                    if (_currNValueDic.ContainsKey(computing.Name))
                    {
                        _currNValueDic[computing.Name].Calc(computing);
                    }
                }
            }
        }
        void NValueChange(List<string> names)
        {
            if (OnNvalueChangeCallback != null && names.Count > 0)
            {
                var dic = new Dictionary<string, long>();
                foreach (var name in names)
                {
                    dic.Add(name, (long)GetNVValue(name));
                }
                OnNvalueChangeCallback(dic);
            }
        }

        public void SetAllNVValue(Dictionary<string, long> nvvalueDic)
        {
            lock (this)
            {
                var changeNames = new List<string>();
                foreach (var key in nvvalueDic.Keys)
                {
                    if (_nvalueDic.ContainsKey(key))
                    {
                        _nvalueDic[key].Value = nvvalueDic[key];
                        changeNames.Add(key);
                    }
                }
                InitCurrNValue();
                CalcNValue();

                NValueChange(changeNames);
            }
        }
        public Dictionary<string, long> GetAllNVValue(bool? save = null, comm.objst.NValueBehaviorType? viewType = null)
        {
            lock (this)
            {
                var dic = new Dictionary<string, long>();
                var nvalueDic = _world.DefineFile.GetAllNValue(_group, save, viewType);
                foreach (var key in nvalueDic.Keys)
                {
                    if (_currNValueDic.ContainsKey(key))
                    {
                        dic.Add(key, _currNValueDic[key].Value);
                    }
                }
                return dic;
            }
        }
        public void AddComputing(string key, comm.objst.NValueComputing computing)
        {
            lock (this)
            {
                if (_nvalueDic.ContainsKey(computing.Name))
                {
                    if (!_computingDic.ContainsKey(key))
                    {
                        _computingDic.Add(key, new List<comm.objst.NValueComputing>());
                    }
                    _computingDic[key].Add(computing);
                    CalcNValue();

                    NValueChange(new List<string> { computing.Name });
                }
            }
        }
        public void AddComputing(string key, string name, string expr)
        {
            var c = comm.objst.NValueComputing.Parse(expr);
            if (c != null)
            {
                c.Name = name;
                AddComputing(key, c);
            }
        }
        public void RemoveComputing(string key)
        {
            lock (this)
            {
                if (_computingDic.ContainsKey(key))
                {
                    var changeNames = new List<string>();
                    var list = _computingDic[key];
                    foreach (var item in list)
                    {
                        changeNames.Add(item.Name);
                    }
                    _computingDic.Remove(key);
                    CalcNValue();

                    NValueChange(changeNames);
                }
            }
        }
        public long? UpdateNVValue(comm.objst.NValueComputing computing)
        {
            comm.objst.NValue nv;
            if (_nvalueDic.TryGetValue(computing.Name, out nv))
            {
                nv.Calc(computing);
                CalcNValue();
                NValueChange(new List<string> { computing.Name });
                return nv.Value;
            }
            Console.WriteLine("UpdateNValue NValue name:" + computing.Name + " not fround!");
            return null;
        }
        public long? UpdateNVValue(string name, string expr)
        {
            var c = comm.objst.NValueComputing.Parse(expr);
            if (c != null)
            {
                c.Name = name;
                UpdateNVValue(c);
            }
            Console.WriteLine("UpdateNValue NValue name:" + name + " not fround!");
            return null;
        }
        public long? GetNVValue(string name)
        {
            comm.objst.NValue nv;
            if (_currNValueDic.TryGetValue(name, out nv))
            {
                return nv.Value;
            }
            Console.WriteLine("GetNValue NValue name:" + name + " not fround!");
            return null;
        }
        public long? SetNVValue(string name, long value)
        {
            comm.objst.NValue nv;
            if (_nvalueDic.TryGetValue(name, out nv))
            {
                nv.Value = value;
                CalcNValue();
                NValueChange(new List<string> { name });
                return nv.Value;
            }
            Console.WriteLine("SetNValue NValue name:" + name + " not fround!");
            return null;
        }
        public long? TrySetNVValue(string name, object longValue)
        {
            try
            {
                SetNVValue(name, long.Parse(longValue.ToString()));
            }
            catch (Exception ex)
            {
                Console.WriteLine("NValueManager TrySetNValue(" + name + ", " + longValue + ")" + ex.Message, comm.ConsoleColor.RED);
            }
            return null;
        }
    }
}
